package com.PuzGame.combinenumbers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Stack;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Vibrator;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;

import com.PuzGame.combinenumbers.Interfaces.GeneralSettingsObserver;
import com.PuzGame.combinenumbers.Interfaces.IListChange;
import com.PuzGame.combinenumbers.customViews.FlowLayout;
import com.PuzGame.combinenumbers.customViews.RationalView;
import com.google.gson.Gson;

public class Level {

	public enum LevelStatus {
		CREATED,
		IN_PROGRESS,
		DONE_CLEARED,
		DONE_GAVE_UP,
		DONE_TIMEOUT
	}

	public enum LevelType {
		ARCADE,
		CHALLENGER,
		CHALLENGEE
	}

	private ArrayList<Long> ListOfNumbers;
	private transient  ArrayList<RationalView> ListOfRationalView;
	private	transient  Stack<UnDoStackItem> UnduStack;
	private long Goal;
	private transient RationalView SelectedRV;
	private transient int SelecteOperation;
	private int qty;
	private int Stage;
	protected transient Context context;
	private transient PopupWindow dpw;
	private StringBuilder Solution;
	public transient IListChange callback;

	protected LevelStatus Status;

	protected LevelType Type;
	
	public String ToJson()
	{
		Gson gson = new Gson();
		return gson.toJson(this);
	}

	public static Level fromJson(String json, Class<?> class1) {
		Level lvl=null;
		Gson gson = new Gson();
		try {
			lvl = (Level) gson.fromJson(json, class1);
		} catch (Exception e) {
			// TODO: handle exception
		}
		return lvl;
	}

	public Level(int level) {
		Stage = level;

		Status= LevelStatus.CREATED;

		Type = LevelType.ARCADE;

		if(level<=12)
		{
			qty = (int)((level/4)+2);
		}
		else
		{
			qty = (int) ((level/5)+2.6);
		}

		Goal = LevelGenerator.RandomNumber(1,2*level+10);

		Solution = new StringBuilder ();
		ListOfNumbers = LevelGenerator.ListFromGoal(qty, Goal, Solution);

		long seed = System.nanoTime();
		Collections.shuffle(ListOfNumbers, new Random(seed));
	}

	public Level(int level, Context c) {
		this(level);

		context = c;
		callback = (IListChange) c;
		
		Status= LevelStatus.CREATED;

		Type = LevelType.ARCADE;


		SelecteOperation = -1;

		ListOfRationalView = new ArrayList<RationalView>();
		UnduStack = new Stack<UnDoStackItem>();

		ResetLevel();
	}


	public Level(int level,long goal,ArrayList<Long> listOfNumbers, String sol, Context c) {

		context = c;
		callback = (IListChange) c;
		Stage = level;
		SelecteOperation = -1;
		
		Status= LevelStatus.CREATED;

		Type = LevelType.ARCADE;

		Solution = new StringBuilder(sol);

		if(level<=12)
		{
			qty = (int)((level/4)+2);
		}
		else
		{
			qty = (int) ((level/5)+2.6);
		}

		Goal = goal;

		ListOfNumbers = listOfNumbers;
		ListOfRationalView = new ArrayList<RationalView>();
		UnduStack = new Stack<UnDoStackItem>();

		if(c!= null)
			ResetLevel();
	}


	public LevelStatus getStatus() {
		return Status;
	}

	public LevelType getType() {
		return Type;
	}

	public void setStatus(LevelStatus status) {
		this.Status = status;
	}

	public long getGoal() {
		// TODO Auto-generated method stub
		return Goal;
	}

	public String getSolution() {
		// TODO Auto-generated method stub
		if(Solution!= null)
			return Solution.toString();
		return "";
	}

	public int getStage() {
		// TODO Auto-generated method stub
		return Stage;
	}

	public ArrayList<Long> getListOfNumbers() {
		// TODO Auto-generated method stub
		return ListOfNumbers;
	}


	public boolean ResetLevel() {

		if(ListOfRationalView.size() == qty)
			return false;
		ClearNumbers();

		UnduStack.clear();

		for (long tempInt : ListOfNumbers)
		{
			AddNumber(new RationalNumber(tempInt, 1));
		}

		SelectedRV = null;
		callback.StackIsEmpty();
		SetOperation(-1);
		return true;
	}






	public void SelectFirstNumber(RationalView TVToSelect) {

		if(SelectedRV != null)
		{
			//SelectedRV.setTextColor(Color.WHITE);
			//SelectedRV.setTextSize(30);
			SelectedRV.SetAsNotSelected();
		}

		if( TVToSelect != null)
		{
			SelectedRV = TVToSelect;
			//SelectedRV.setTextColor(Color.RED);
			//SelectedRV.setTextSize(35);
			SelectedRV.SetAsSelected();
		}

		SetOperation(-1);


	}

	public void SelectSecondNumber(RationalView RVToOPWith) {

		if(SelectedRV == null || RVToOPWith == null || SelecteOperation == -1)
		{
			return;
		}

		if(SelectedRV == RVToOPWith)
		{
			//SelectedRV.setTextColor(Color.WHITE);
			//SelectedRV.setTextSize(30);
			SelectedRV.SetAsNotSelected();
			SelectedRV = null;
			SetOperation(-1);

			return;
		}

		RationalNumber first = SelectedRV.rn_data;
		RationalNumber second = RVToOPWith.rn_data;




		RationalNumber result = new RationalNumber(0,1);

		switch (SelecteOperation) {
		case 0:
			result = first.add(second);
			break;

		case 1:
			result = first.subtract(second);
			break;

		case 2:
			result = first.multiply(second);
			break;

		case 3:
			if(second.equals(new RationalNumber(0,1)))
			{
				Toast.makeText(context,
						context.getString(R.string.divide_by_zero), Toast.LENGTH_LONG)
						.show();
				return;
			}

			result = first.divide(second);
			break;

		}

		if(Math.abs(result.getNumerator())>10000000)
		{
			int StringId = 0;
			if(result.getDenominator() == 1)
			{
				if(result.getNumerator()>0)
				{
					StringId = R.string.number_too_big;
				}
				else
				{
					StringId = R.string.number_too_small;
				}
			}
			else
			{
				if(result.getNumerator()>0)
				{
					StringId = R.string.numerato_too_big;
				}
				else
				{
					StringId = R.string.numerato_too_small;
				}
			}

			Toast.makeText(context,
					String.format(context.getString(StringId), result.getNumerator()),
					Toast.LENGTH_LONG)
					.show();

			SelectedRV.SetAsNotSelected();
			SelectedRV = null;
			SetOperation(-1);
			return;
		}

		if(Math.abs(result.getDenominator())>10000000)
		{
			int StringId = 0;
			if(result.getDenominator()>0)
			{
				StringId = R.string.denominator_too_big;
			}
			else
			{
				StringId = R.string.denominator_too_small;
			}
			Toast.makeText(context,
					String.format(context.getString(StringId), result.getDenominator()),
					Toast.LENGTH_LONG)
					.show();

			SelectedRV.SetAsNotSelected();
			SelectedRV = null;
			SetOperation(-1);
			return;
		}




		UnDoStackItem udsi = new UnDoStackItem();

		RationalView rv = AddNumber(result);




		udsi.FirstPos = PositionOfNumber(SelectedRV);
		udsi.FirstRationalView = SelectedRV;
		RemoveNumber(SelectedRV);


		udsi.SecondtPos = PositionOfNumber(RVToOPWith);
		udsi.SecondRationalView = RVToOPWith;
		RemoveNumber(RVToOPWith);





		UnduStack.push(udsi);

		callback.StackHasItems();

		SelectedRV = null;
		SetOperation(-1);

		if(callback.getQuickPlayMode())
			SelectFirstNumber(rv);//TODO

		callback.OpDone();
		CheckIfStageIsClear();

	}






	private RationalView CreateNewRV(Context context,  RationalNumber ratnum) {

		final RationalView rv = new RationalView(context,ratnum);
		rv.setTextSize(25);

		rv.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {

				callback.vibrate(50);
				callback.playSound(R.raw.button_pressed);

				if(SelectedRV == null || SelecteOperation == -1)
					SelectFirstNumber(rv);
				else
					SelectSecondNumber(rv);

			}
		});

		rv.setOnLongClickListener(new OnLongClickListener() {

			@Override
			public boolean onLongClick(View v) {
				ArrayList<Long> Factors = new ArrayList<Long>();
				long NumberToDivide = 0;
				long NumberToMultiply = 0;
				if(rv.rn_data.getDenominator() != 1)
				{
					NumberToMultiply = rv.rn_data.getDenominator();
					long biggestNumerator = 0;
					for (RationalView rv : ListOfRationalView) 
					{
						if(!rv.equals(v) && Math.abs(rv.rn_data.getNumerator())>biggestNumerator)
						{
							biggestNumerator = Math.abs(rv.rn_data.getNumerator());
						}
					}
					if(biggestNumerator/NumberToMultiply > 0)
					{
						initiateMultipliersPopupWindow(NumberToMultiply, ((long) (biggestNumerator*1.1))/NumberToMultiply);
					}
					else
					{
						Toast.makeText(Level.this.context,
								String.format(Level.this.context.getString(R.string.no_multipliers),Math.abs(NumberToDivide))
								, Toast.LENGTH_LONG)
								.show();
					}
				}
				else
				{
					NumberToDivide = rv.rn_data.getNumerator();
					if(LevelGenerator.isPrime(Math.abs(NumberToDivide)))
					{
						Toast.makeText(Level.this.context,
								String.format(Level.this.context.getString(R.string.prime_num),Math.abs(NumberToDivide))
								, Toast.LENGTH_LONG)
								.show();
					}
					else
					{
						Factors = LevelGenerator.getfactors(Math.abs(NumberToDivide));
						if(Factors != null && Factors.size() > 0)
						{
							Collections.sort(Factors);
							initiateDividersPopupWindow(NumberToDivide, Factors);
						}
					}
				}




				return true;
			}
		});


		return rv;

	}

	private void initiateDividersPopupWindow(long numberForHeader, ArrayList<Long> NumList) {

		LayoutInflater inflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		View layout = inflater.inflate(R.layout.dividers_pop_win,
				(ViewGroup) ((Activity)context).findViewById(R.id.dividersPopWin));
		WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
		lp.width = WindowManager.LayoutParams.MATCH_PARENT;
		lp.height = WindowManager.LayoutParams.MATCH_PARENT;
		dpw = new PopupWindow(layout, lp.width, lp.height, true);
		dpw.showAtLocation(layout, Gravity.CENTER_VERTICAL, 0, 0);


		ImageButton btncancel = (ImageButton) layout.findViewById(R.id.button_close);

		FlowLayout fl = (FlowLayout) layout.findViewById(R.id.DividerList);

		int i = 0;
		int SizeOfDividers = 15;

		TextView tvTitle = (TextView) layout.findViewById(R.id.NumberToDivideTV);


		tvTitle.setText(String.format(context.getString(R.string.dividers), Math.abs(numberForHeader)));


		for (i = 0; i < NumList.size(); i++)
		{
			RationalNumber rn = new RationalNumber(NumList.get(i), 1);
			boolean found = false;
			for (RationalView rv : ListOfRationalView) 
			{
				if(rv.rn_data.equals(rn) || rv.rn_data.equals(rn.multiply(new RationalNumber(-1,1))))
				{
					RationalView rvToAdd = new RationalView(context, rn);
					rvToAdd.setTextSize(SizeOfDividers);
					fl.addView(rvToAdd);
					found = true;
					break;
				}
			}
			if(!found)
			{
				TextView tv = new TextView(context);
				tv.setTextSize(SizeOfDividers);
				tv.setTypeface(null, Typeface.BOLD);	
				tv.setText(NumList.get(i).toString());
				fl.addView(tv);
			}
		}

		LinearLayout llFiller = new LinearLayout(context);
		llFiller.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 15));
		fl.addView(llFiller);

		btncancel.setOnClickListener(dividers_cancel_click);

	}

	private void initiateMultipliersPopupWindow(long numberForHeader, long qty) {

		LayoutInflater inflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		View layout = inflater.inflate(R.layout.dividers_pop_win,
				(ViewGroup) ((Activity)context).findViewById(R.id.dividersPopWin));
		WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
		lp.width = WindowManager.LayoutParams.MATCH_PARENT;
		lp.height = WindowManager.LayoutParams.MATCH_PARENT;
		dpw = new PopupWindow(layout, lp.width, lp.height, true);
		dpw.showAtLocation(layout, Gravity.CENTER_VERTICAL, 0, 0);


		ImageButton btncancel = (ImageButton) layout.findViewById(R.id.button_close);

		FlowLayout fl = (FlowLayout) layout.findViewById(R.id.DividerList);


		TextView tvTitle = (TextView) layout.findViewById(R.id.NumberToDivideTV);



		FillMultipliersPage(numberForHeader, qty, fl, 1, tvTitle);

		btncancel.setOnClickListener(dividers_cancel_click);

	}

	public void FillMultipliersPage(final long numberForHeader, final long qty,
			final FlowLayout fl, final int Page, final TextView tvTitle) {

		final int itemsPerPage = (int) Math.min(200,qty);
		int tempNumOfPages = (int) (qty/itemsPerPage);
		if(qty%itemsPerPage>0)
			tempNumOfPages++;

		final int NumOfPages = tempNumOfPages;

		int SizeOfDividers = 15;
		fl.removeAllViews();

		tvTitle.setText(String.format(context.getString(R.string.multipliers), Math.abs(numberForHeader),Page,NumOfPages));

		if(Page!=1)
		{
			ImageView iv = new ImageView(context);
			iv.setLayoutParams(new LayoutParams(100,100));
			iv.setImageResource(R.drawable.left);
			iv.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					int tempPage = Page-1;
					FillMultipliersPage(numberForHeader, qty,
							fl, tempPage, tvTitle);
				}
			});
			iv.setOnLongClickListener(new OnLongClickListener() {

				@Override
				public boolean onLongClick(View v) {
					FillMultipliersPage(numberForHeader, qty,
							fl, 1, tvTitle);
					return true;
				}
			});
			fl.addView(iv);
		}

		for (long i = Math.max(1,(Page-1)*itemsPerPage); i <= itemsPerPage*Page && i <= qty; i++)
		{
			RationalNumber rn = new RationalNumber(i*numberForHeader, 1);
			boolean found = false;
			for (RationalView rv : ListOfRationalView) 
			{
				if(rv.rn_data.equals(rn) || rv.rn_data.equals(rn.multiply(new RationalNumber(-1,1))))
				{
					RationalView rvToAdd = new RationalView(context, rn);
					rvToAdd.setTextSize(SizeOfDividers);
					fl.addView(rvToAdd);
					found = true;
					break;
				}
			}
			if(!found)
			{
				TextView tv = new TextView(context);
				tv.setTextSize(SizeOfDividers);
				tv.setTypeface(null, Typeface.BOLD);	
				tv.setText(String.valueOf(i*numberForHeader));
				fl.addView(tv);
			}
		}

		if(NumOfPages!=Page)
		{
			ImageView iv = new ImageView(context);
			LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(100,100);
			iv.setLayoutParams(lp);
			iv.setImageResource(R.drawable.right);
			iv.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					int tempPage = Page+1;
					FillMultipliersPage(numberForHeader, qty,
							fl, tempPage, tvTitle);
				}
			});
			iv.setOnLongClickListener(new OnLongClickListener() {

				@Override
				public boolean onLongClick(View v) {
					FillMultipliersPage(numberForHeader, qty,
							fl, NumOfPages, tvTitle);
					return true;
				}
			});
			fl.addView(iv);
		}
		LinearLayout llFiller = new LinearLayout(context);
		llFiller.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 15));
		fl.addView(llFiller);

	}


	private OnClickListener dividers_cancel_click = new OnClickListener()
	{
		public void onClick(View v) {
			dpw.dismiss();

		}
	};
	

	private RationalView AddNumber(RationalNumber ratnum) 
	{
		RationalView rv = CreateNewRV(context,ratnum);
		ListOfRationalView.add(rv);
		callback.AddView(rv);
		return rv;
	}

	private RationalView AddNumber(RationalNumber ratnum, int pos) 
	{
		RationalView rv = CreateNewRV(context,ratnum);
		ListOfRationalView.add(pos, rv);
		callback.AddView(pos, rv);
		return rv;
	}

	public void RemoveNumber(RationalView rv) 
	{
		ListOfRationalView.remove(rv);
		callback.RemoveView(rv);
	}

	public void RemoveLastNumber() 
	{
		if(ListOfRationalView.size() == 0)
			return;

		ListOfRationalView.remove(ListOfRationalView.size()-1);
		callback.RemoveLastView();
	}



	private void ClearNumbers() 
	{
		ListOfRationalView.clear();
		callback.ClearAll();
	}

	private int PositionOfNumber(RationalView rv) 
	{
		return ListOfRationalView.indexOf(rv);
	}

	private boolean CheckIfStageIsClear() {
		if(	CheckForLastNumber() &&
				ListOfRationalView.get(0).rn_data.IsNatural() &&
				ListOfRationalView.get(0).rn_data.getNumerator() == Goal)
		{
			ListOfRationalView.get(0).setTextColor(Color.BLUE);
			ListOfRationalView.get(0).setTextSize(50);
			callback.StageCleared();
			return true;
		}
		return false;
	}

	private boolean CheckForLastNumber() {
		if(	ListOfRationalView.size() == 1)
		{
			ListOfRationalView.get(0).setOnClickListener(null);
			return true;
		}
		return false;
	}



	public void SetOperation(int i) {
		if(SelectedRV == null && i != -1)
			return;

		if(SelecteOperation == i && i !=- 1)
		{
			SetOperation(-1);
			return;
		}
		
		if(i !=- 1)
		{
			callback.vibrate(70);
			callback.playSound(R.raw.button_pressed);
		}
		SelecteOperation = i;
		callback.SelectOperation(i);
	}



	public boolean Undo() 
	{
		if(UnduStack.empty())
			return false;
		UnDoStackItem udsi = UnduStack.pop();

		AddNumber(udsi.SecondRationalView.rn_data,udsi.SecondtPos);
		AddNumber(udsi.FirstRationalView.rn_data,udsi.FirstPos);
		RemoveLastNumber();

		if(UnduStack.isEmpty())
		{
			callback.StackIsEmpty();
		}
		return true;


	}
	
	public String StoreLevel(Context c, String name) {

		SharedPreferences settings = c.getSharedPreferences(name, c.MODE_PRIVATE);
		SharedPreferences.Editor editor = settings.edit();

		String res = this.ToJson();
		editor.putString("CompleteLevel", res);
		
		editor.commit();
		
		Log.i("SharedPreferences", "Stored level:"+ res);
		
		return res;
	}


	public boolean IsDone() {
		
		return Status == LevelStatus.DONE_CLEARED || Status == LevelStatus.DONE_GAVE_UP || Status == LevelStatus.DONE_TIMEOUT;
	}


}

final class UnDoStackItem 
{
	public int FirstPos;
	public int SecondtPos;
	public RationalView FirstRationalView;
	public RationalView SecondRationalView;
}

